package com.hatricks.is.base.stream.progressinputstream;

import cn.hutool.extra.spring.SpringUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @packageName: com.glodon.qydata.archive.service.progress
 * @className: ProgressContext
 * @author: yanyh yanyh@glodon.com
 * @date: 2024-04-08 21:13
 * @description: 文件上传进度条进行流控
 */
@Slf4j
public class ProgressContext {

    /**
     * 添加定时任务线程池进行进度的刷新,与前端保持逻辑一致 使用时间秒进行数据流的刷新
     */
    public static final ScheduledThreadPoolExecutor SCHEDULED_THREAD_POOL_EXECUTOR;
    public static final long ONE_HUNDRED_PER = 100L;
    private static final long NINE_NINE_NUMBER_PER = 99L;

    static {
        SCHEDULED_THREAD_POOL_EXECUTOR = SpringUtil.getBean("doc-process-thread-pool");
    }

    public ProgressContext(Long totalSize) {
        this.totalSize = totalSize;
    }

    public ProgressContext(Long totalSize, Boolean controlFinished) {
        this.totalSize = totalSize;
        finished.compareAndSet(finished.get(), controlFinished);
    }

    /**
     * 文件总大小
     */
    @Getter
    private final Long totalSize;
    /**
     * 上传进度结束(匹配有可能存在的业务回调 手动指示上传完成) （是否存在业务回调）
     */
    private final AtomicBoolean finished = new AtomicBoolean(Boolean.FALSE);
    /**
     * 实时块进度
     */
    @Getter
    private final AtomicLong chunkProgress = new AtomicLong(0);
    /**
     * 已上传的总大小
     */
    @Getter
    private final AtomicLong uploadedSize = new AtomicLong(0L);
    /**
     * 任务定时调度
     */
    private ScheduledFuture<?> scheduledTask;
    /**
     * 异常信号量
     */
    private final AtomicBoolean exceptionSignal = new AtomicBoolean(Boolean.FALSE);

    public synchronized AtomicBoolean getExceptionSignal() {
        return exceptionSignal;
    }

    public synchronized void buildException() {
        exceptionSignal.compareAndSet(exceptionSignal.get(), Boolean.TRUE);
        cancelSchedule();
    }

    public void startProcess(Runnable task) {
        scheduledTask = SCHEDULED_THREAD_POOL_EXECUTOR.scheduleAtFixedRate(task, 0, 1, TimeUnit.SECONDS);
    }

    public void finished() {
        finished.compareAndSet(finished.get(), Boolean.FALSE);
    }

    /**
     * 在业务线程中建立块监听（确保使用的ProgressContext一个对象）
     *
     * @param chunkSize 块大小
     */
    public synchronized void buildChunkProgress(long chunkSize) {
        chunkProgress.getAndAdd(buildCurrentProgress());
        uploadedSize.set(0);
        log.info("当前已有一个文件上传完毕,文件大小为:[{}]", chunkSize);
    }

    /**
     * 在定时任务线程中进行进度计算（计算时进行对象监听）
     *
     * @return 当前上传进度
     */
    public synchronized long calculateUploadProgress() {
        // 在上传间隙中的时候直接返回块进度条
        long currentProgress = chunkProgress.get() + buildCurrentProgress();
        if (currentProgress >= ONE_HUNDRED_PER) {
            if (finished.get()) {
                return NINE_NINE_NUMBER_PER;
            }
            currentProgress = ONE_HUNDRED_PER;
            cancelSchedule();
        }
        return currentProgress;
    }

    private long buildCurrentProgress() {
        return ProgressUtils.calculateProgress(uploadedSize.get(), totalSize);
    }

    /**
     * 正常取消以及异常取消(都可取消定时调度)
     */
    public void cancelSchedule() {
        this.scheduledTask.cancel(Boolean.TRUE);
    }


}
